﻿/*
		DynamicParams.inc
		Copyright © 2012 RyDeR`
*/

#if defined _Included_DynamicParams
	#endinput
#else
	#define _Included_DynamicParams
#endif

#if !defined MAX_DYNAMIC_PARAMS
	#define MAX_DYNAMIC_PARAMS (128)
#endif

#include "a_samp.inc"

static stock
	g_aiStack[MAX_DYNAMIC_PARAMS],
	g_iIdx = 0
;

enum e_Types {
	Mixed,
	String,
	ByRef
};

stock Push_Param(const e_Types: iType, { Float, _ }: ...) {
	if(numargs() == 2) {
		if(g_iIdx < MAX_DYNAMIC_PARAMS) {
			static
				s_iAddr = -1
			;
			if(s_iAddr == -1) {
				// Get variable address
				#emit CONST.PRI		g_aiStack
				#emit STOR.PRI		s_iAddr
			}
			if(s_iAddr != -1) {
				new
					iTemp
				;
				// Adjust variable address to current index
				#emit LOAD.PRI		s_iAddr
				#emit LOAD.ALT		g_iIdx
				#emit ADD
				#emit STOR.S.PRI	iTemp
				
				switch(iType) {
					case Mixed: {
						// Store value in g_aiStack
						#emit LOAD.S.ALT	iTemp
						#emit LOAD.S.PRI	16
						#emit LOAD.I
						#emit STOR.I
					}
					case ByRef, String: {
						// Store address in g_aiStack
						#emit LOAD.S.ALT	iTemp
						#emit LOAD.S.PRI	16
						#emit STOR.I
					}
				}
				// Increase the index
				#emit LOAD.PRI		g_iIdx
				#emit ADD.C			4
				#emit STOR.PRI		g_iIdx
				
				return ((g_iIdx - 4) >>> 2);
			}
		}
	}
	return -1;
}

stock Set_Param(const e_Types: iType, iIdx, { Float, _ }: ...) {
	if(numargs() == 3) {
		if(iIdx < MAX_DYNAMIC_PARAMS) {
			static
				s_iAddr = -1
			;
			if(s_iAddr == -1) {
				// Get variable address
				#emit CONST.PRI		g_aiStack
				#emit STOR.PRI		s_iAddr
			}
			if(s_iAddr != -1) {
				new
					iTemp
				;
				iIdx = iIdx << 2;
				
				// Adjust variable address to current index
				#emit LOAD.PRI		s_iAddr
				#emit LOAD.S.ALT	iIdx
				#emit ADD
				#emit STOR.S.PRI	iTemp
				
				switch(iType) {
					case Mixed: {
						// Store value in g_aiStack
						#emit LOAD.S.ALT	iTemp
						#emit LOAD.S.PRI	20
						#emit LOAD.I
						#emit STOR.I
					}
					case ByRef, String: {
						// Store address in g_aiStack
						#emit LOAD.S.ALT	iTemp
						#emit LOAD.S.PRI	20
						#emit STOR.I
					}
				}
				return (iIdx >>> 2);
			}
		}
	}
	return -1;
}

stock Pop_Param() {
	if(g_iIdx > 0) {
		// Decrease the index
		#emit LOAD.PRI		g_iIdx
		#emit CONST.ALT		4
		#emit SUB
		#emit STOR.PRI		g_iIdx
		
		return 1;
	}
	return 0;
}

stock CallFunction(const szName[], const bool: bPop = true) {
	new
		iParam,
		iAddr,
		iIdx = funcidx(szName)
	;
	if(iIdx != -1) {
		// Get function's address - Slice
		#emit LCTRL			1
		#emit NEG
		#emit MOVE.ALT
		#emit ADD.C			32
		#emit STOR.S.PRI	iParam
		#emit LREF.S.PRI	iParam
		#emit ADD
		#emit LOAD.S.ALT	iIdx
		#emit SHL.C.ALT		3
		#emit ADD
		#emit STOR.S.PRI	iParam
		#emit LREF.S.PRI	iParam
		#emit STOR.S.PRI	iAddr
		
		iIdx = g_iIdx >>> 2;
		
		// Push everything in the stack
		while (iIdx--) {
			iParam = g_aiStack[iIdx];
			#emit PUSH.S iParam
		}
		// Push number of parameters in bytes
		#emit LOAD.PRI		g_iIdx
		#emit PUSH.PRI
		
		// Push return address
		#emit LCTRL			6
		#emit ADD.C			28
		#emit PUSH.PRI
		
		// Call function
		#emit LOAD.S.PRI	iAddr
		#emit SCTRL			6
		
		// Clear the stack and return the final value
		#emit STACK			12
		#emit RETN
		
		if(bPop) {
			while(Pop_Param() != 0) {
				continue;
			}
		}
	}
	return -1;
}